mod is_file_matched;

use crate::entry::{Entry, save_entries};
use anyhow::Result;
use is_file_matched::get_matched;
use std::fs::{self};
use std::path::Path;
use walkdir::WalkDir;

/// 搜索文档并将结果保存到.search文件
///
/// # Arguments
/// * `keyword` - 搜索关键字
/// * `config` - 应用配置
/// * `working_dir` - 工作目录，即搜索的开始的目录
///
/// # Returns
/// 返回`Result<()>`，成功时返回`Ok(())`，失败时返回错误
pub fn search_documents(keyword: &str, working_dir: &Path) -> Result<()> {
    // 1. 根据关键字和目录搜索文档
    let entries = search_files(keyword, working_dir)?;

    // 2. 将结果保存到.search文件
    save_entries(&entries, working_dir)?;

    Ok(())
}

/// 检查是否为系统文件或目录
///
/// # Arguments
/// * `file_path` - 文件路径
///
/// # Returns
/// 返回`bool`，如果是系统文件则返回true，否则返回false
fn is_system_file(file_path: &Path) -> bool {
    // 常见的系统文件和目录模式
    let system_patterns = [
        ".git",
        ".DS_Store",
        "Thumbs.db",
        "__pycache__",
        "node_modules",
        "target",
        "Cargo.lock",
        "package-lock.json",
        ".idea",
        ".vscode",
    ];

    // 检查文件名或路径是否包含系统模式
    if let Some(file_name) = file_path.file_name() {
        let file_name_str = file_name.to_string_lossy();

        for pattern in system_patterns.iter() {
            if file_name_str.contains(pattern) {
                return true;
            }
        }
    }

    // 检查完整路径是否包含系统目录
    let path_str = file_path.to_string_lossy();
    for pattern in system_patterns.iter() {
        if path_str.contains(pattern) {
            return true;
        }
    }

    false
}

/// 实际执行文件搜索
///
/// # Arguments
/// * `keyword` - 搜索关键字
/// * `working_dir` - 搜索目录
/// * `config` - 应用配置
///
/// # Returns
/// 返回`Result<Vec<Entry>>`，成功时返回包含搜索结果的向量，失败时返回错误
fn search_files(keyword: &str, working_dir: &Path) -> Result<Vec<Entry>> {
    let mut entries = Vec::new();

    for entry in WalkDir::new(working_dir)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.file_type().is_file())
        .filter(|e| !is_system_file(e.path()))
    // 跳过系统文件
    {
        let file_path = entry.path();

        // 记录调试信息
        log::debug!("Searching file: {}", file_path.display());

        let content = match fs::read_to_string(file_path) {
            Ok(content) => content,
            Err(e) => {
                // 记录读取文件失败的错误，但不中断整个搜索过程
                log::error!("Failed to read file {}: {}", file_path.display(), e);
                continue;
            }
        };

        if let Some(matched_content) = get_matched(&content, keyword) {
            let relative_path = file_path
                .strip_prefix(working_dir)
                .unwrap_or(file_path)
                .to_path_buf();

            entries.push(Entry {
                file_path: relative_path.display().to_string(),
                matched_content,
                created_at: chrono::Utc::now(),
            });
        }
    }

    log::info!("Search completed. Found {} matching files.", entries.len());
    Ok(entries)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::{self, File};
    use std::path::PathBuf;

    #[test]
    fn test_is_system_file() {
        // 测试系统文件识别
        assert!(is_system_file(Path::new(".git/config")));
        assert!(is_system_file(Path::new("project/.DS_Store")));
        assert!(is_system_file(Path::new("src/__pycache__/module.pyc")));
        assert!(is_system_file(Path::new("node_modules/package/index.js")));
        assert!(is_system_file(Path::new("target/debug/app")));

        // 测试非系统文件
        assert!(!is_system_file(Path::new("src/main.rs")));
        assert!(!is_system_file(Path::new("README.md")));
        assert!(!is_system_file(Path::new("docs/guide.txt")));
    }
}
